﻿/**************************************************************************************
 * ValidatorHelper.cs
 * An extension utility class to add validation functionality to web input controls
 * Jaime Alberto Jaramillo Zapata (jjaramillo.informatica-tecnologia.com)
 * Created on 07/02/2012.
 * Last update on 08/02/2012.
 **************************************************************************************/

using System.Web.UI;
using System.Web.UI.WebControls;
using AjaxControlToolkit;
using System;

namespace MS.InformationManagement.Support
{
    /// <summary>
    /// An extension utility class to add validation functionality to web input controls
    /// </summary>
    public static class ValidatorHelper
    {
        /// <summary>
        /// Adds a <see cref="RequiredFieldValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/> to which the required field validator is added to.</param>
        /// <param name="text">The error message to appear when the input is empty and the validation is triggered.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <returns></returns>
        public static TextBox Required(this TextBox control, string text, ValidatorCalloutPosition position)
        {
            return control.Required(text, string.Empty, position);
        }

        /// <summary>
        /// Adds a <see cref="RequiredFieldValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/> to which the required field validator is added to.</param>
        /// <param name="text">The error message to appear when the input is empty and the validation is triggered.</param>
        /// <param name="validationGroupName">Name of the validation group.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <returns></returns>
        public static TextBox Required(this TextBox control, string text, string validationGroupName, ValidatorCalloutPosition position)
        {
            var valid = new RequiredFieldValidator { ErrorMessage = text, ControlToValidate = control.ID, ID = string.Format("req_{0}", control.ID), ValidationGroup = validationGroupName, Text = "*", Display = ValidatorDisplay.Static };
            var callOutExtender = new ValidatorCalloutExtender { ID = string.Format("call_{0}", valid.ID), TargetControlID = valid.ID, PopupPosition = position };
            AddCustomValidator(control, valid, callOutExtender);
            return control;
        }

        /// <summary>
        /// Adds a <see cref="RequiredFieldValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup.
        /// </summary>
        /// <param name="control">The target <see cref="DropDownList"/>  to which the required field validator is added to.</param>
        /// <param name="text">The error message to appear when the input is empty and the validation is triggered.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <returns></returns>
        public static DropDownList Required(this DropDownList control, string text, ValidatorCalloutPosition position)
        {
            return control.Required(text, string.Empty, position);
        }

        /// <summary>
        /// Adds a <see cref="RequiredFieldValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup.
        /// </summary>
        /// <param name="control">The target <see cref="DropDownList"/>  to which the required field validator is added to.</param>
        /// <param name="text">The error message to appear when the input is empty and the validation is triggered.</param>
        /// <param name="validationGroupName">Name of the validation group.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <returns></returns>
        public static DropDownList Required(this DropDownList control, string text, string validationGroupName, ValidatorCalloutPosition position)
        {
            var valid = new RequiredFieldValidator { ID = string.Format("req_{0}", control.ID), ErrorMessage = text, ControlToValidate = control.ID, InitialValue = "0", ValidationGroup = validationGroupName, Text = "*", Display = ValidatorDisplay.Static };
            var callOutExtender = new ValidatorCalloutExtender { ID = string.Format("call_{0}", valid.ID), TargetControlID = valid.ID, PopupPosition = position };
            AddCustomValidator(control, valid, callOutExtender);
            return control;
        }

        public static DropDownList Required(this DropDownList control, string text, string validationGroupName, ValidatorCalloutPosition position, bool causeValidation)
        {
            control.CausesValidation = causeValidation;
            var valid = new RequiredFieldValidator { ID = string.Format("req_{0}", control.ID), ErrorMessage = text, ControlToValidate = control.ID, InitialValue = "0", ValidationGroup = validationGroupName, Text = "*", Display = ValidatorDisplay.Static };
            var callOutExtender = new ValidatorCalloutExtender { ID = string.Format("call_{0}", valid.ID), TargetControlID = valid.ID, PopupPosition = position };
            AddCustomValidator(control, valid, callOutExtender);
            return control;
        }

        public static ListBox Required(this ListBox control, string text, string validationGroupName, ValidatorCalloutPosition position)
        {
            var valid = new RequiredFieldValidator { ID = string.Format("req_{0}", control.ID), ErrorMessage = text, ControlToValidate = control.ID, InitialValue = "", ValidationGroup = validationGroupName, Text = "*", Display = ValidatorDisplay.Static };
            var callOutExtender = new ValidatorCalloutExtender { ID = string.Format("call_{0}", valid.ID), TargetControlID = valid.ID, PopupPosition = position };
            AddCustomValidator(control, valid, callOutExtender);
            return control;
        }

        public static ListBox Required(this ListBox control, string text, ValidatorCalloutPosition position)
        {
            return control.Required(text, string.Empty, position);
        }

        /// <summary>
        /// Adds a <see cref="RangeValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the required range validator is added to.</param>
        /// <param name="min">The minimum value to be used in the range validator.</param>
        /// <param name="max">The maximum value to be used in the range validator.</param>
        /// <param name="text">The error message to appear when the input is above the max value and/or the minimum
        /// value.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <returns></returns>
        public static TextBox MinMax(this TextBox control, long? min, long? max, string text, ValidatorCalloutPosition position)
        {
            return control.MinMax(min, max, text, string.Empty, position);
        }

        /// <summary>
        /// Adds a <see cref="RangeValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the required range validator is added to.</param>
        /// <param name="min">The minimum value to be used in the range validator.</param>
        /// <param name="max">The maximum value to be used in the range validator.</param>
        /// <param name="text">The error message to appear when the input is above the max value and/or the minimum
        /// value.</param>
        /// <param name="validationGroupName">Name of the validation group.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <returns></returns>
        public static TextBox MinMax(this TextBox control, long? min, long? max, string text, string validationGroupName, ValidatorCalloutPosition position)
        {
            var valid = new RangeValidator
            {
                ID = string.Format("ran_{0}",
                control.ID),
                ErrorMessage = text,
                ControlToValidate = control.ID,
                MinimumValue = min.HasValue ? min.ToString() : string.Empty,
                MaximumValue = max.HasValue ? max.ToString() : string.Empty,
                ValidationGroup = validationGroupName,
                Type = ValidationDataType.Double,
                Text = "*"
            };
            var callOutExtender = new ValidatorCalloutExtender { ID = string.Format("call_{0}", valid.ID), TargetControlID = valid.ID, PopupPosition = position };
            AddCustomValidator(control, valid, callOutExtender);
            return control;
        }

        /// <summary>
        /// Adds a <see cref="RegularExpressionValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the regular expression validator is added to.</param>
        /// <param name="regularExpression">The regular expression to be used in the validator.</param>
        /// <param name="text">The error message to appear when the input does not match the regular expression.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <returns></returns>
        public static TextBox RegularExpression(this TextBox control, string regularExpression, string text, ValidatorCalloutPosition position)
        {
            return control.RegularExpression(regularExpression, text, string.Empty, position);
        }

        /// <summary>
        /// Adds a <see cref="RegularExpressionValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the regular expression validator is added to.</param>
        /// <param name="regularExpression">The regular expression to be used in the validator.</param>
        /// <param name="text">The error message to appear when the input does not match the regular expression.</param>
        /// <param name="validationGroupName">Name of the validation group.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <returns></returns>
        public static TextBox RegularExpression(this TextBox control, string regularExpression, string text, string validationGroupName, ValidatorCalloutPosition position)
        {
            var valid = new RegularExpressionValidator
            {
                ID = string.Format("rev_{0}", control.ID),
                ErrorMessage = text,
                ControlToValidate = control.ID,
                ValidationExpression = regularExpression,
                ValidationGroup = validationGroupName,
                Text = "*"
            };
            var callOutExtender = new ValidatorCalloutExtender { ID = string.Format("call_{0}", valid.ID), TargetControlID = valid.ID, PopupPosition = position };
            AddCustomValidator(control, valid, callOutExtender);
            return control;
        }

        /// <summary>
        /// Adds a numerical <see cref="MaskEditExtender"/> to the target control.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/> to which the numerical mask is going to be applied.</param>
        /// <param name="numericMask">The numeric mask to be applied.</param>
        /// <param name="isCurrency">if set to <c>true</c> displays the current culture's currency symbol to the left.</param>
        /// <returns></returns>
        public static TextBox Numeric(this TextBox control, string numericMask, bool isCurrency)
        {
            var extender = new MaskedEditExtender
            {
                TargetControlID = control.ID,
                ID = string.Format("meenum_{0}", control.ID),
                Mask = numericMask,
                MaskType = MaskedEditType.Number,
                AcceptNegative = MaskedEditShowSymbol.None,
                DisplayMoney = isCurrency ? MaskedEditShowSymbol.Left : MaskedEditShowSymbol.None,
                AutoComplete = false,
                ClearMaskOnLostFocus = true,
                ClearTextOnInvalid = true,
                InputDirection= MaskedEditInputDirection.LeftToRight
            };
            AddCustomExtender(control, extender);

            return control;
        }

        /// <summary>
        /// Adds a numerical With Separator<see cref="MaskEditExtender"/> to the target control.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/> to which the numerical mask is going to be applied.</param>
        /// <param name="numericMask">The numeric mask to be applied.</param>
        /// <param name="isCurrency">if set to <c>true</c> displays the current culture's currency symbol to the left.</param>
        /// <returns></returns>
        public static TextBox NumericWithSeparator(this TextBox control)
        {
            var extender = new FilteredTextBoxExtender
            {
                TargetControlID = control.ID,
                ID = string.Format("meenum_{0}", control.ID),
                FilterType = FilterTypes.Custom | FilterTypes.Numbers,
                ValidChars=",."
             };
            string separadorMiles=System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyGroupSeparator;
            string separadorDecimal= System.Globalization.CultureInfo.CurrentCulture.NumberFormat.CurrencyDecimalSeparator;
            control.RegularExpression(string.Format(Support.Constants.RegularExpresions.EXP_DECIMAL, separadorMiles, separadorDecimal), string.Format("El valor ingresado debe cumplir con el siguiente formato ###{0}###{1}00",separadorMiles,separadorDecimal), AjaxControlToolkit.ValidatorCalloutPosition.BottomRight);
            AddCustomExtender(control, extender);
            return control;
        }

        /// <summary>
        /// Adds an ajax <see cref="CalendarExtender"/> to the target control.
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the calendar extender is appended to.</param>
        /// <param name="dateTimeFormat">The date time format to be used in the calendar extender.</param>
        /// <param name="popUpButtonId">The pop up button id which triggers the calendar extender.</param>
        /// <param name="calendarPosition">The calendar extender's position relative to the target text box.</param>
        /// <returns></returns>
        public static TextBox DateTime(this TextBox control, string dateTimeFormat, string popUpButtonId, CalendarPosition calendarPosition)
        {
            var extender = new CalendarExtender
            {
                TargetControlID = control.ID,
                ID = string.Format("ce_{0}", control.ID),
                Format = dateTimeFormat,
                PopupButtonID = popUpButtonId,
                PopupPosition = calendarPosition
            };
            AddCustomExtender(control, extender);
            return control;
        }

        /// <summary>
        /// Adds a <see cref="CompareValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the compare validator is added to.</param>
        /// <param name="target">The target <see cref="TextBox"/> to compare against.</param>
        /// <param name="compareOperator">The compare operator used.</param>
        /// <param name="text">The error message to appear when the inputs don't match the compare validator rules.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <param name="dataType">Type of the data which is being compared.</param>
        /// <returns></returns>
        public static TextBox Compare(this TextBox control, TextBox target, ValidationCompareOperator compareOperator, string text
            , ValidatorCalloutPosition position, ValidationDataType dataType)
        {
            return control.Compare(target, compareOperator, text, string.Empty, position, dataType);
        }

        /// <summary>
        /// Adds a <see cref="CompareValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the compare validator is added to.</param>
        /// <param name="target">The target <see cref="TextBox"/> to compare against.</param>
        /// <param name="compareOperator">The compare operator used.</param>
        /// <param name="text">The error message to appear when the inputs don't match the compare validator rules.</param>
        /// <param name="validationGroupName">Name of the validation group.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <param name="dataType">Type of the data which is being compared.</param>
        /// <returns></returns>
        public static TextBox Compare(this TextBox control, TextBox target, ValidationCompareOperator compareOperator, string text, string validationGroupName
            , ValidatorCalloutPosition position, ValidationDataType dataType)
        {
            var valid = new CompareValidator
            {
                ID = string.Format("cv_{0}_{1}_{2:N}", control.ID, target.ID, Guid.NewGuid()),
                ErrorMessage = text,
                Operator = compareOperator,
                ControlToValidate = control.ID,
                ControlToCompare = target.ID,
                Type = dataType,
                ValidationGroup = validationGroupName,
                Text = "*"
            };
            var callOutExtender = new ValidatorCalloutExtender { ID = string.Format("call_{0}", valid.ID), TargetControlID = valid.ID, PopupPosition = position };
            AddCustomValidator(control, valid, callOutExtender);
            return control;
        }

        /// <summary>
        /// Adds a <see cref="CompareValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the compare validator is added to.</param>
        /// <param name="value">The value to use during the compare operation</param>
        /// <param name="compareOperator">The compare operator used.</param>
        /// <param name="text">The error message to appear when the inputs don't match the compare validator rules.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <param name="dataType">Type of the data which is being compared.</param>
        /// <returns></returns>
        public static TextBox Compare(this TextBox control, string value, ValidationCompareOperator compareOperator, string text
            , ValidatorCalloutPosition position, ValidationDataType dataType)
        {
            return control.Compare(value, compareOperator, text, string.Empty, position, dataType);
        }

        /// <summary>
        /// Adds a <see cref="CompareValidator"/> to the target control and wrapps it inside a <see cref="ValidatorCalloutExtender"/>
        /// popup
        /// </summary>
        /// <param name="control">The target <see cref="TextBox"/>  to which the compare validator is added to.</param>
        /// <param name="value">The value to use during the compare operation</param>
        /// <param name="compareOperator">The compare operator used.</param>
        /// <param name="text">The error message to appear when the inputs don't match the compare validator rules.</param>
        /// <param name="validationGroupName">Name of the validation group.</param>
        /// <param name="position">The position in which the ajax <see cref="ValidatorCalloutExtender"/> appears.</param>
        /// <param name="dataType">Type of the data which is being compared.</param>
        /// <returns></returns>
        public static TextBox Compare(this TextBox control, string value, ValidationCompareOperator compareOperator, string text, string validationGroupName
            , ValidatorCalloutPosition position, ValidationDataType dataType)
        {
            var valid = new CompareValidator
            {
                ID = string.Format("cvo_{0}", control.ID),
                ErrorMessage = text,
                Operator = compareOperator,
                ControlToValidate = control.ID,
                ValueToCompare = value,
                Type = dataType,
                ValidationGroup = validationGroupName,
                Text = "*"
            };
            var callOutExtender = new ValidatorCalloutExtender { ID = string.Format("call_{0}", valid.ID), TargetControlID = valid.ID, PopupPosition = position };
            AddCustomValidator(control, valid, callOutExtender);
            return control;
        }

        /// <summary>
        /// Adds a custom ajax extender to the target control's control collection.
        /// </summary>
        /// <param name="control">The target control.</param>
        /// <param name="extender">The extender to be added.</param>
        private static void AddCustomExtender(Control control, ExtenderControlBase extender)
        {
            control.Parent.Controls.Add(extender);
        }

        /// <summary>
        /// Adds a custom validator to the target control's control collection.
        /// </summary>
        /// <param name="control">The target control.</param>
        /// <param name="valid">The validator to be added.</param>
        /// <param name="extender">The ajax extender in which the validator is wrapped in.</param>
        private static void AddCustomValidator(Control control, BaseValidator valid, ValidatorCalloutExtender extender)
        {
            PlaceHolder validatorPlaceHolder = control.Parent.FindControl("validatorPlaceHolder") as PlaceHolder;
            if (validatorPlaceHolder == null)
            {
                validatorPlaceHolder = new PlaceHolder { ID = "validatorPlaceHolder" };
                control.Parent.Controls.Add(validatorPlaceHolder);
            }

            valid.CssClass = "validator";
            valid.Display = ValidatorDisplay.None;
            valid.PreRender += (sender, e) =>
            {
                validatorPlaceHolder.Controls.Add(extender);
            };
            int index = control.Parent.Controls.IndexOf(control);
            control.Parent.Controls.AddAt(index + 1, valid);

        }
    }
}
